package com.example.redis_delay_queue;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Set;

@Component
public class RedisDelayQueue {

    private static final String QUEUE_KEY = "delay_queue";
    private static final String LOCK_PREFIX = "delay_queue_lock_";
    private static final long LOCK_EXPIRE_TIME = 30 * 1000; // 锁过期时间为 30 秒

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 队列入队
     *
     * @param payload 有效载荷
     * @param delay   延迟多久后开始执行
     */
    @Async("taskExecutor")
    public void enqueue(Object payload, long delay) {
        // 计算延迟时间戳
        long timestamp = System.currentTimeMillis() + delay;
        // 添加到延迟队列
        redisTemplate.opsForZSet().add(QUEUE_KEY, payload, timestamp);
    }

    /**
     * 取出并处理队列中的任务
     */
    @Async("taskExecutor")
    public void dequeueAndHandle() {
        while (true) {
            // 获取当前时间戳
            long timestamp = System.currentTimeMillis();
            // 获取队列中的任务，使用 WITHS-CORES 选项获取任务的分数（即延迟时间戳）
            Set<Object> payloads = redisTemplate.opsForZSet().rangeByScore(QUEUE_KEY, 0, timestamp, 0, 10);
            assert payloads != null;
            if (payloads.isEmpty()) {
                // 队列为空，等待一段时间后继续检查
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 忽略中断异常
                }
            } else {
                for (Object payload : payloads) {
                    try {
                        String lockKey = LOCK_PREFIX + payload.toString();
                        boolean locked = false;
                        long startTime = System.currentTimeMillis();
                        int retryCount = 0; // 记录重试次数
                        while (!locked) {
                            // 尝试获取分布式锁
                            locked = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", Duration.ofDays(LOCK_EXPIRE_TIME)));
                            // 获取锁成功
                            if (locked) {
                                // 成功获取锁，处理队列中的任务
                                // ...
                                // 处理完成后，从队列中删除任务
                                redisTemplate.opsForZSet().remove(QUEUE_KEY, payload);
                                // 释放锁
                                redisTemplate.delete(lockKey);
                            }
                            // 获取锁失败～等待一段时间后重试
                            else if (!locked) {
                                long elapsedTime = System.currentTimeMillis() - startTime;
                                long remainingTime = LOCK_EXPIRE_TIME - elapsedTime;
                                if (remainingTime > 0) {
                                    Thread.sleep(Math.min(remainingTime, 100));
                                }
                                retryCount++;
                            }
                            // 获取锁失败～重试次数超过 3 次，放弃处理任务
                            else  if (retryCount > 3){
                                // 记录日志
                                // 释放锁
                                redisTemplate.delete(lockKey);
                            }
                            else {
                                //其他
                            }
                        }
                    } catch (Exception e) {
                        // 处理任务出现异常，记录日志
                    }
                }
            }
        }
    }
}
